#ifndef CANDRIVER_H
#define CANDRIVER_H
#include <concepts>
#include <format>
#include <functional>
#include <memory>
#include <optional>
#include <linux/can.h>

#include "DriverConfig.h"

namespace canpp {
    class CanSender;
}

namespace canpp {
    class CanReader;
}

namespace canpp {
    using std::unique_ptr;
    using std::string;
    using std::optional;
    using std::nullopt;
    using std::function;
    static bool loggingConfigured = false;
    void loggingConfigure(LoggingConfig&& config = {});



    template<typename T>
    concept CanFrameConvertible = requires(T a)
    {
        { static_cast<can_frame>(a) } -> std::same_as<can_frame>;
        { static_cast<T>(can_frame{}) } -> std::same_as<T>;
    };
#if false // example of CanFrameConvertible
    struct ConvertibleType {
        operator can_frame() const {
            return can_frame{};
        }

        MyType(const can_frame&) {
        }
    };
#endif

    template<CanFrameConvertible MsgT=can_frame>
    class CanDriver {
    public:
        ~CanDriver();

        CanDriver() = delete;

        explicit CanDriver(CanDriverConfig &&config);

        void registerReadingCallback(std::function<void(MsgT &&)> &func);

        void registerReadingCallback(std::function<void(MsgT &&)> &&func);

        MsgT canPeekMessage();

        void canSendMessage(const MsgT &&msg, optional<uint16_t> &&canId = nullopt);

    private:
        string id;
        string interface;
        unique_ptr<CanReader> reader = nullptr;
        unique_ptr<CanSender> sender = nullptr;
    };
}


#endif //CANDRIVER_H
